/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "RegHelper.h"
#include "Mcal_Delay.h"
#include "Sdrv_I2c.h"
#include "I2c_Fault.h"

#define I2C_START_SEC_CODE_FAST
#include "I2c_MemMap.h"

/**
 * Reads a byte of data from the I2C RX FIFO.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return The byte of data read from the RX FIFO.
 */
uint8 sdrv_i2c_lld_recv_data(uint32 base)
{
    return (uint8)REG_READ32(base + I2C_RXFIFO);
}

/**
 * Sends a byte of data to the I2C TX FIFO.
 *
 * @param base The base address of the I2C peripheral.
 * @param data The byte of data to send.
 */
void sdrv_i2c_lld_send_data(uint32 base, uint8 data)
{
    REG_WRITE32((uint32)data, base + I2C_TXFIFO);
}

/**
 * @brief Get PSR register status
 *
 * Read the status value of the PSR register from the given I2C controller base
 * address and PSR register offset
 *
 * @param base  I2C controller base address
 * @return uint32_t value of PSR register
 */
uint32 sdrv_i2c_get_lld_psr0_stat(uint32 base)
{
    uint32 regVal = REG_READ32(base + I2C_PSR0);
    I2cHookBegin_I2c_ErrorPsr0();
    return regVal;
}

/**
 * Gets the available write space in the I2C FIFO.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return The available write space in the I2C FIFO.
 */
uint32 sdrv_i2c_lld_get_wspace(uint32 base)
{
    return (REG_READ32(base + I2C_FSR) >> I2C_FSR_EMPTYLVL_BIT) & I2C_FSR_EMPTYLVL_MASK;
}

/**
 * Gets the available read space in the I2C FIFO.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return The available read space in the I2C FIFO.
 */
uint32 sdrv_i2c_lld_get_rspace(uint32 base)
{
    return (REG_READ32(base + I2C_FSR) >> I2C_FSR_FILLLVL_BIT) & I2C_FSR_FILLLVL_MASK;
}
#define I2C_STOP_SEC_CODE_FAST
#include "I2c_MemMap.h"

#define I2C_START_SEC_CODE
#include "I2c_MemMap.h"
/**
 * Sets the I2C watermark levels for the TX and RX FIFOs.
 *
 * @param base The base address of the I2C peripheral.
 * @param tx_lvl The watermark level for the TX FIFO.
 * @param rx_lvl The watermark level for the RX FIFO.
 */
void sdrv_i2c_lld_set_watermark(uint32 base, uint8 tx_lvl,
                                uint8 rx_lvl)
{
    uint32 val;

    val = REG_READ32(base + I2C_FCR);
    val &= ~(I2C_FCR_TXWMLVL_MASK << I2C_FCR_TXWMLVL_BIT);
    val &= ~(I2C_FCR_RXWMLVL_MASK << I2C_FCR_RXWMLVL_BIT);
    val |= ((uint32)(tx_lvl) << I2C_FCR_TXWMLVL_BIT);
    val |= ((uint32)(rx_lvl) << I2C_FCR_RXWMLVL_BIT);
    REG_WRITE32(val, base + I2C_FCR);
}
#define I2C_STOP_SEC_CODE
#include "I2c_MemMap.h"

#define I2C_START_SEC_CODE_FAST
#include "I2c_MemMap.h"

/**
 * Clears the I2C FIFO.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return 0 if the FIFO is cleared successfully, -1 if the operation times out.
 */
int sdrv_i2c_lld_clear_fifo(uint32 base)
{
    uint32 val;
    uint32 timeout = 0U;
    int retval;

    val = REG_READ32(base + I2C_FCR);
    val |= (I2C_FCR_CLRF_MASK << I2C_FCR_CLRF_BIT);
    REG_WRITE32(val, base + I2C_FCR);

    /* waiting for clear fifo finshed */
    do
    {
        /* wait max time 5*pclk + 5*sclk is about 1us */
        val = (REG_READ32(base + I2C_FCR) >> I2C_FCR_CLRF_BIT) & I2C_FCR_CLRF_MASK;
        I2cHookBegin_I2c_ErrorClrFifo();
        /*PRQA S 3415 1*/
    }
    while ((0x1U == val) && (timeout++ < 1000U));

    if (timeout < 1000U)
    {
        retval = 0;
    }
    else
    {
        retval = -1;
    }

    return retval;
}

/**
 * Sets the I2C Command Control and Status Register 0 (I2C_CMDCSR0) with the given value.
 *
 * @param base The base address of the I2C peripheral.
 * @param cmdcsr0 The value to be written to the I2C_CMDCSR0 register.
 *
 * @return None.
 */
void sdrv_i2c_lld_set_cmdcsr0(uint32 base, uint32 cmdcsr0)
{
    REG_WRITE32(cmdcsr0, base + I2C_CMDCSR0);
}

/**
 * Sets the I2C Command Control and Status Register 2 (I2C_CMDCSR2) with the given value.
 *
 * @param base The base address of the I2C peripheral.
 * @param cmdcsr2 The value to be written to the I2C_CMDCSR2 register.
 *
 * @return None.
 */
void sdrv_i2c_lld_set_cmdcsr2(uint32 base, uint32 cmdcsr2)
{
    REG_WRITE32(cmdcsr2, base + I2C_CMDCSR2);
}

/**
 * Clears all interrupts for the I2C peripheral at the given base address.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return None.
 */
void sdrv_i2c_lld_clear_int_all(uint32 base)
{
    REG_WRITE32(~0x0UL, base + I2C_INTR0);
    REG_WRITE32(~0x0UL, base + I2C_INTR1);
    REG_WRITE32(~0x0UL, base + I2C_INTR2);
    REG_WRITE32(~0x0UL, base + I2C_INTR3);
}

/**
 * Clear interrupts for the I2C peripheral at the given base address.
 *
 * @param base The base address of the I2C peripheral.
 * @param intr Interrupt status that needs to be cleared
 *
 * @return None.
 */
void sdrv_i2c_lld_clear_intr0(uint32 base, uint32 intr)
{
    REG_WRITE32(intr, base + I2C_INTR0);
}

/**
 * Retrieves the status of the I2C interrupt register.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return The status of the I2C interrupt register.
 */
uint32 sdrv_i2c_lld_get_int0_stat(uint32 base)
{
    uint32 regVal = REG_READ32(base + I2C_INTR0);
    I2cHookBegin_I2c_ErrorInt0();
    return regVal;
}

/**
 * Unmasks the I2C interrupt 0 for the given base address.
 *
 * @param base The base address of the I2C peripheral.
 * @param int_flag The interrupt flag to be unmasked.
 *
 * @return None.
 */
void sdrv_i2c_lld_unmask_int0(uint32 base, uint32 int_flag)
{
    REG_WRITE32(int_flag, base + I2C_INTEN0);
}

/**
 * Unmasks the I2C interrupt 1 for the given base address.
 *
 * @param base The base address of the I2C peripheral.
 * @param int_flag The interrupt flag to be unmasked.
 *
 * @return None.
 */
void sdrv_i2c_lld_unmask_int1(uint32 base, uint32 int_flag)
{
    REG_WRITE32(int_flag, base + I2C_INTEN1);
}

/**
 * Unmasks the I2C interrupt 2 for the given base address.
 *
 * @param base The base address of the I2C peripheral.
 * @param int_flag The interrupt flag to be unmasked.
 *
 * @return None.
 */
void sdrv_i2c_lld_unmask_int2(uint32 base, uint32 int_flag)
{
    REG_WRITE32(int_flag, base + I2C_INTEN2);
}

/**
 * Unmasks the I2C interrupt 3 for the given base address.
 *
 * @param base The base address of the I2C peripheral.
 * @param int_flag The interrupt flag to be unmasked.
 *
 * @return None.
 */
void sdrv_i2c_lld_unmask_int3(uint32 base, uint32 int_flag)
{
    REG_WRITE32(int_flag, base + I2C_INTEN3);
}
#define I2C_STOP_SEC_CODE_FAST
#include "I2c_MemMap.h"

#define I2C_START_SEC_CODE
#include "I2c_MemMap.h"
/**
 * Sets the I2C bus clear for the given base address.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return None.
 */
void sdrv_i2c_lld_set_busclr(uint32 base)
{
    uint32 val;

    val = REG_READ32(base + I2C_PCR0);
    val |= (I2C_PCR0_BUSCLR_MASK << I2C_PCR0_BUSCLR_BIT);
    REG_WRITE32(val, base + I2C_PCR0);
}

/**
 * Gets the I2C bus clear status for the given base address.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return The I2C bus clear status.
 */
uint32 sdrv_i2c_lld_get_busclr_stat(uint32 base)
{
    return (REG_READ32(base + I2C_PCR0) >> I2C_PCR0_BUSCLR_BIT) & I2C_PCR0_BUSCLR_MASK;
}

/**
 * Sets the speed mode for the I2C peripheral at the given base address.
 *
 * @param base The base address of the I2C peripheral.
 * @param speed_mode The speed mode to be set.
 *
 * @return None.
 */
void sdrv_i2c_lld_set_speed(uint32 base, uint32 speed_mode)
{
    uint32 val;

    val = REG_READ32(base + I2C_PCR0);
    val &= ~(I2C_PCR0_SPEED_MASK << I2C_PCR0_SPEED_BIT);
    val |= (speed_mode << I2C_PCR0_SPEED_BIT);
    REG_WRITE32(val, base + I2C_PCR0);
}

/**
 * Sets the I2C timing configuration for the given base address.
 *
 * @param base The base address of the I2C peripheral.
 * @param pcr2 The value to be written to the I2C PCR2 register.
 * @param pcr3 The value to be written to the I2C PCR3 register.
 * @param pcr4 The value to be written to the I2C PCR4 register.
 * @param pcr5 The value to be written to the I2C PCR5 register.
 *
 * @return None.
 */
void sdrv_i2c_lld_set_timing(uint32 base,
                             uint32 pcr2, uint32 pcr3, uint32 pcr4, uint32 pcr5)
{
    REG_WRITE32(pcr2, base + I2C_PCR2);
    REG_WRITE32(pcr3, base + I2C_PCR3);
    REG_WRITE32(pcr4, base + I2C_PCR4);
    REG_WRITE32(pcr5, base + I2C_PCR5);
}

/**
 * Sets the I2C HS master code.
 *
 * @param base The base address of the I2C peripheral.
 * @param mastercode the HS master code.
 * @return None.
 */
void sdrv_i2c_lld_set_mastercode(uint32 base, uint8 mastercode)
{
    uint32 val;

    val = (mastercode & I2C_PCR12_HS_MASTERCODE_MASK) << I2C_PCR12_HS_MASTERCODE_BIT;
    REG_WRITE32(val, base + I2C_PCR12);
}

/**
 * Sets the operation mode for the I2C peripheral at the given base address.
 *
 * @param base The base address of the I2C peripheral.
 * @param opmode The operation mode to be set.
 *
 * @return None.
 */
void sdrv_i2c_lld_set_opmode(uint32 base, uint32 opmode)
{
    uint32 val;

    val = REG_READ32(base + I2C_MCR0);
    val &= ~(I2C_MCR0_OPMOD_MASK << I2C_MCR0_OPMOD_BIT);
    val |= (opmode << I2C_MCR0_OPMOD_BIT);
    REG_WRITE32(val, base + I2C_MCR0);
}

/**
 * Disables the I2C module at the specified base address.
 *
 * @param base The base address of the I2C module.
 *
 * @return None.
 */
void sdrv_i2c_lld_disable(uint32 base)
{
    uint32 val;

    val = REG_READ32(base + I2C_MCR0);
    val &= ~(I2C_MCR0_MODEN_MASK << I2C_MCR0_MODEN_BIT);
    REG_WRITE32(val, base + I2C_MCR0);
}

/**
 * Enables the I2C module at the specified base address.
 *
 * @param base The base address of the I2C module.
 *
 * @return None.
 */
void sdrv_i2c_lld_enable(uint32 base)
{
    uint32 val;

    val = REG_READ32(base + I2C_MCR0);
    val |= (I2C_MCR0_MODEN_MASK << I2C_MCR0_MODEN_BIT);
    REG_WRITE32(val, base + I2C_MCR0);
}

/**
 * Resets the I2C peripheral at the given base address.
 *
 * @param base The base address of the I2C peripheral.
 *
 * @return 0 if the reset is successful, -1 if the operation times out.
 */
int sdrv_i2c_lld_reset(uint32 base)
{
    uint32 timeout = 0U;
    uint32 val;
    sint32 retval;

    val = REG_READ32(base + I2C_MCR0);
    val |= (I2C_MCR0_MODRST_MASK << I2C_MCR0_MODRST_BIT);
    REG_WRITE32(val, base + I2C_MCR0);

    /* waiting for reset finished */
    do
    {
        /* wait max time 5*pclk + 5*sclk is about 1us */
        /*PRQA S 2985 1*/
        val = (REG_READ32(base + I2C_MCR0) >> I2C_MCR0_MODRST_BIT) & I2C_MCR0_MODRST_MASK;
        I2cHookBegin_I2c_ErrorReset();
        /*PRQA S 3415 1*/
    }
    while ((0x1U == val) && (timeout++ < 1000U));

    if (timeout < 1000U)
    {
        retval = 0;
    }
    else
    {
        retval = -1;
    }

    return retval;
}
#define I2C_STOP_SEC_CODE
#include "I2c_MemMap.h"
